package com.nbsaas.boot.otp.ext.resource;

import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.codec.binary.Base32;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
 
public class OtpService {
 
    private static final int TIME_STEP = 30; // 时间步长（秒）
    private static final int OTP_LENGTH = 6; // OTP 长度
    private static final int MAX_ATTEMPTS = 5; // 最大尝试次数
    private static final long BLOCK_DURATION = 300_000; // 封锁时间（毫秒）
 
    private final ConcurrentHashMap<String, AtomicInteger> attemptCounter = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Long> blockedUsers = new ConcurrentHashMap<>();
 
    // 生成 TOTP
    public String generateTOTP(String secret) throws Exception {
        long timeIndex = Instant.now().getEpochSecond() / TIME_STEP;
        return generateOtp(secret, timeIndex);
    }
 
    // 验证 TOTP
    public boolean validateTOTP(String secret, String otp, String userId) throws Exception {
        if (isBlocked(userId)) {
            System.out.println("User is temporarily blocked: " + userId);
            return false;
        }
 
        long timeIndex = Instant.now().getEpochSecond() / TIME_STEP;
 
        // 在验证窗口内检查 OTP
        for (int i = -1; i <= 1; i++) {
            String generatedOtp = generateOtp(secret, timeIndex + i);
            if (generatedOtp.equals(otp)) {
                resetAttempts(userId);
                return true;
            }
        }
 
        recordFailedAttempt(userId);
        return false;
    }
 
    private String generateOtp(String secret, long timeIndex) throws Exception {
        // 解码 Base32 密钥
        Base32 base32 = new Base32();
        byte[] keyBytes = base32.decode(secret);
 
        // 转换时间索引为字节数组
        byte[] timeBytes = ByteBuffer.allocate(8).putLong(timeIndex).array();
 
        // 使用 HMAC-SHA1 生成哈希
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "HmacSHA1");
        mac.init(keySpec);
        byte[] hash = mac.doFinal(timeBytes);
 
        // 提取动态偏移量
        int offset = hash[hash.length - 1] & 0x0F;
        int binary = ((hash[offset] & 0x7F) << 24) | ((hash[offset + 1] & 0xFF) << 16)
                | ((hash[offset + 2] & 0xFF) << 8) | (hash[offset + 3] & 0xFF);
 
        // 生成 OTP
        int otp = binary % (int) Math.pow(10, OTP_LENGTH);
        return String.format("%0" + OTP_LENGTH + "d", otp);
    }
 
    private void recordFailedAttempt(String userId) {
        attemptCounter.putIfAbsent(userId, new AtomicInteger(0));
        int attempts = attemptCounter.get(userId).incrementAndGet();
 
        if (attempts >= MAX_ATTEMPTS) {
            blockedUsers.put(userId, System.currentTimeMillis());
            System.out.println("User blocked due to multiple failed attempts: " + userId);
        }
    }
 
    private void resetAttempts(String userId) {
        attemptCounter.remove(userId);
        blockedUsers.remove(userId);
    }
 
    private boolean isBlocked(String userId) {
        Long blockTime = blockedUsers.get(userId);
        if (blockTime == null) {
            return false;
        }
 
        if (System.currentTimeMillis() - blockTime > BLOCK_DURATION) {
            blockedUsers.remove(userId);
            return false;
        }
        return true;
    }
}